헬로, 안드로이드 11 주차 위치파악하기와감지하기 강대기동서대학교컴퓨터정보공학부
학습목표 GPS 장치를통해위치를인식하는방법에대해서알아본다. 가속도계에대해서알아본다. 지도를나타내는맵뷰에대해알아본다. 웹뷰와맵뷰를결합함으로써, 여러서비스들을결합하는매시업 (mashup) 에대해알아본다.
차례 위치, 위치, 위치 센서를최대로설정하기 조감도 웹뷰와맵뷰 요약 퀴즈 연습문제
위치, 위치, 위치 위치정보 Global Positioning System (GPS) GPS 소스가어떤것인가에따라해상도가다르지만, 2009년현재기본적으로 10~25m 정도의오차를가짐 내비게이션은자체알고리즘으로이를보정함 그외에도 Differential GPS 등과같은다양한구조개선이있음 2000년 5월이전에는, 미국의군사적보안을이유로, 민간용은일부러 100m 오차를가지게했었음 (Selective Availability) 한국에서위치정보를제공하는사업자에게는국가기관이위치정보내역을요구할수있음 ( 아이폰출시문제, 개인정보보호문제 ) 인근휴대폰기지국 Wifi 핫스팟 LocationTest 프로젝트 (org.example.locationtest) AndroidManifest.xml 에다음추가 <uses-permission android:name="android.permission.access_coarse_location" /> <uses-permission android:name="android.permission.access_fine_location" />
res/layout/main.xml <ScrollView xmlns:android="http://schemas.android.com/a pk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent"> <TextView android:id="@+id/output" android:layout_width="fill_parent" android:layout_height="wrap_content" /> </ScrollView>
LocationTest.onCreate() @Override public void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); setcontentview(r.layout.main); mgr = (LocationManager) getsystemservice(location_service); output = (TextView) findviewbyid(r.id.output); log("location providers:"); dumpproviders(); Criteria criteria = new Criteria(); best = mgr.getbestprovider(criteria, true); log("\nbest provider is: " + best); log("\nlocations (starting with last known):"); Location location = mgr.getlastknownlocation(best); dumplocation(location);
LocationTest.onCreate() getsystemservice(location_service) 는 LocationManager 클래스반환 dumpproviders() 는위치정보제공자들출력 getbestprovider(criteria, true) 에서원래는비용, 전력, 정확성등에대해선정기준을조정할수있음
위치업데이트 LocationManager.requestLocationUpdates() 호출하면위치변화를안드로이드가알려줌 배터리절약을위해프로그램이 foreground 인경우에만위치업데이트 onresume() 과 onpause() @Override protected void onresume() { super.onresume(); // Start updates (doc recommends delay >= 60000 ms) mgr.requestlocationupdates(best, 15000, 1, this); @Override protected void onpause() { super.onpause(); // Stop updates to save power while app paused mgr.removeupdates(this);
리스너객체 vs. 액티비티에서구현 리스너객체를새로만드는대신, 액티비티에참조를넘기면 1KB 메모리절약 public void onlocationchanged(location location) { dumplocation(location); public void onproviderdisabled(string provider) { log("\nprovider disabled: " + provider); public void onproviderenabled(string provider) { log("\nprovider enabled: " + provider); public void onstatuschanged(string provider, int status, Bundle extras) { log("\nprovider status changed: " + provider + ", status=" + S[status] + ", extras=" + extras); 장치위치변경을제공자가알아챌때마다, onlocationchanged() 메서드가호출됨
에뮬레이터는어떻게하나? 근본적으로에뮬레이터는 Fake GPS 제공자를사용함 Dalvik Debug Monitor Service (DDMS) 를이용하는에뮬레이터컨트롤에서경도와위도입력 창 > 보기뷰 > 기타 > 안드로이드 > 에뮬레이터컨트롤 Window > Show View > Other > Android > Emulator Control 안드로이드에뮬레이터컨솔 (telnet 127.1 5554) 에뮬레이터컨트롤에서 Google Earth 에서출력된 KML 파일입력 DDMS 를이용하는외부프로그램사용
에뮬레이터컨트롤에서 GPS 값입력
에뮬레이터컨솔에서 GPS 값입력
LocationTest 실행결과
센서를최대로설정하기 레이싱게임의경우의예 닌텐도 DS 버튼을이용해서좌회전, 우회전 안드로이드폰, 아이폰, 닌텐도 Wii Accelerometer( 가속도계 ) 사용 참여를유발하는센서 SENSOR_ACCELEROMETER x,y,z 좌표의가속측정 SENSOR_LIGHT 주위영역의밝기를알려줌 SENSOR_MAGNETIC_FIELD x,y,z, 축에전자극을반환함 SENSOR_ORIENTATION 장치가한쪽으로쏠리고, 던져지고, 돌려지는것을측정함 SENSOR_ORIENTATION_RAW SENSOR_ORIENTATION 에서필터링이빠진것 SENSOR_PROXIMITY 센서와객체사이의거리를알려줌 SENSOR_TEMPERATURE 주위의온도를측정함 SENSOR_TRICORDER 장치를영화스타트렉의트라이코더와같이만듦 SensorManager 클래스는, LocationManager 와유사하나, 업데이트가몇백분의일초정도로빠르게됨.
SensorTest.java 센서에접근하기위해서는 getsystemservice() 메서드호출함 private SensorManager mgr; private TextView output; private List<Sensor> sensorlist; @Override public void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); setcontentview(r.layout.main); //... mgr = (SensorManager) getsystemservice(sensor_service); output = (TextView) findviewbyid(r.id.output);
SensorTest.java 센서서비스는값이변경될때마다 onsensorchanged() 를호출함 for (int i = 0; i < event.values.length; i++) { builder.append(event.values[i]);
센서들을사용하는프로그래밍의경우 모든센서는부동소수점명령을반환함 반환되는배열의크기는센서의특성에따라다름 센서, 특히가속도계의값을쓸모있는정보로하려면많은어려움이있음 가속도계는수치가불안함. 평활화 (smoothing) 를통해부드럽게만들어야하지만, 지나치면인터페이스가처지는느낌이남. 센서숫자는임의의개수가함께나오는데, 한번에여러개가나오기도하고, 잠시멈췄다가다시여럿이전송되기도함 사용자가다음에무엇을입력할지미리예측해한발앞서야함. 연이은세개의값이오른쪽이라면, 다음값도어느정도예측하고있어야함. 센서의가장난이도있는사용의예는, 사용자가장치를움직이는것과스크린에서일어나는반응이일대일로연결되는액션게임 ( 에뮬레이터상에서는테스트가어려움 )
Sensor Simulator SensorTest 프로그램을그냥실행하면아무런결과도나오지않음 www.openintents.org 에서대체센서 API 를제공함 www.openintents.org 의 Sensor Simulator 를다운받아에뮬레이터와연결하면, 시뮬레이터에서는가상폰의이미지를보여주고, 마우스로움직이게해주며, 그움직임을에뮬레이터에있는안드로이드프로그램에넘김 애플맥북에는센서가내장되어있으며, 센서가없는컴퓨터의경우, 닌텐도 Wii 를연결해도됨 대신프로그램소스를고쳐야함
Sensor Simulator 설치방법 최신버전의 sensorsimulator- 버전.zip 을다운받아임의의디렉토리에풀음 (http://code.google.com/p/openintents/downloads /list) 다운받아푼시뮬레이터의 bin 디렉토리에있는일반자바응용프로그램인 SensorSimulator.jar 를실행한후, IP 주소, 포트및센서옵션들을설정 SensorSimulatorSettings.apk 를에뮬레이터에설치한후, SensorSimulatorSettings 에서동일하게 IP 주소, 포트, 및센서들의옵션설정 samples 디렉토리에있는 SensorDemo (OISensorDemo) 를이클립스에임포트 (import) 하여, 버전을 1.5 로설정하고, 안드로이드에서실행함
Sensor Simulator 사용예
조감도 Google 맵스 Ajax 기술을통해 ( 자바스크립트와 XmlHttpRequest 객체를이용해플러그인없이 ) 어떤브라우저에서도드래그할수있고, 확대 / 축소할수있는, 지도뷰어 맵뷰컨트롤임베딩 MyMap / org.example.mymap / MyMap / MyMap Build target 은 Google API 1.6 으로설정 AVD 도 Google API 1.6 용으로디바이스하나만듦 레이아웃을맵뷰로대체함 (main.xml) MapAPIKey 부분은사용자가스스로 API Key 를받아서바꾸어주어야함
main.xml <LinearLayout xmlns:android="http://schemas.android.com/apk/res/ android" android:id="@+id/frame" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent"> <com.google.android.maps.mapview android:id="@+id/map" android:apikey="mapapikey" android:layout_width="fill_parent" android:layout_height="fill_parent" android:clickable="true" /> </LinearLayout>
Google MAP API 디버깅용 Key 얻기 디버깅용 API Key 이클립스를이용할경우 debug.keystore 의위치는 Window > Preference > Android > Build 탭의 default debug keystore 에있음 keytool -list -keystore debug.keystore 위치를입력하여 MD5 fingerprint 생성 http://code.google.com/intl/ko/android/maps-api-signup.html 에가서 Terms and Conditions 를체크하고, MD5 fingerprint 를입력하고, Generate API Key 버튼누름 C:\Android\android-sdk-windows-1.5_r3\tools>keytool -list -keystore C:\Users\DK\.android\debug.keystore keystore 암호를입력하십시오 : Keystore 유형 : JKS Keystore 공급자 : SUN Keystore 에는 1 항목이포함되어있습니다. androiddebugkey, 2009. 9. 10, PrivateKeyEntry, 인증서지문 (MD5): CB:76:29:11:B5:72:8F:08:E6:70:86:92:DF:23:EC:C9 C:\Android\android-sdk-windows-1.5_r3\tools>
AndroidManifest 에 MapView 추가 <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="org.example.mymap" android:versioncode="1" android:versionname="1.0"> <uses-permission android:name="android.permission.access_coarse_location" /> <uses-permission android:name="android.permission.access_fine_location" /> <uses-permission android:name="android.permission.internet" /> <application android:icon="@drawable/icon" android:label="@string/app_name"> <activity android:name=".mymap" android:label="@string/app_name"> <intent-filter> <action android:name="android.intent.action.main" /> <category android:name="android.intent.category.launcher" /> </intent-filter> </activity> <uses-library android:name="com.google.android.maps" /> </application> <uses-sdk android:minsdkversion= 4" /> </manifest>
MyMap 실행화면
MyMap 클래스개요 MapActivity 확장 findviewbyid() 로지도에대한접근을얻고, MapView.getController() 로컨트롤러을얻음 MapView.setBuiltInZoomControls() 로확대 / 축소컨트롤설정 initmylocation() 내에서 MyLocationOverlay 포인터를얻고 MyLocationOverlay.enableMyLocation() 로현재위치의업데이트를받도록함 MyLocationOverlay.runOnFirstFix() 는위치제공자로부터처음정보를제공받으면오버레이가무엇을해야하는지지정해줌 실행하면, 폰의경우에는붉은점이사용자의위치를따라다님. 에뮬레이터의경우에는 GPS 정보를입력해줘야함. 구글맵스에서지질학적정보와이미지들은안드로이드코어와같이오픈소스로제공되는것이아니라, 유료지도데이터제공자들로부터구글을통해다른방식으로제공되는것이므로예를들면 android.maps 패키지아래있지않고, com.google.android.maps 패키지로제공됨
MyMap.java (1/2) package org.example.mymap; import android.os.bundle; import com.google.android.maps.mapactivity; import com.google.android.maps.mapcontroller; import com.google.android.maps.mapview; import com.google.android.maps.mylocationoverlay; public class MyMap extends MapActivity { private MapView map; private MapController controller; @Override public void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); setcontentview(r.layout.main); initmapview(); initmylocation(); @Override protected boolean isroutedisplayed() { // Required by MapActivity return false;
MyMap.java (2/2) /** Find and initialize the map view. */ private void initmapview() { map = (MapView) findviewbyid(r.id.map); controller = map.getcontroller(); map.setsatellite(true); map.setbuiltinzoomcontrols(true); /** Start tracking the position on the map. */ private void initmylocation() { final MyLocationOverlay overlay = new MyLocationOverlay(this, map); overlay.enablemylocation(); //overlay.enablecompass(); // does not work in emulator overlay.runonfirstfix(new Runnable() { public void run() { // Zoom in to current location controller.setzoom(8); controller.animateto(overlay.getmylocation()); ); map.getoverlays().add(overlay);
웹뷰와맵뷰 맵뷰에웹뷰를추가함 main.xml 에서 android:layout_height="150px" 으로고정해서설정후, 하단에 뷰안의웹 예제를가져다삽입 MyMapWeb.onCreate() 에 뷰안의웹 예제를가져다삽입 뷰안의웹 예제의 openbrowser() 추가
main.xml <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/frame" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent"> <com.google.android.maps.mapview android:id="@+id/map" android:apikey= " MapAPIKey" android:layout_width="fill_parent" android:layout_height="150px" android:clickable="true" /> <LinearLayout android:orientation="horizontal" android:layout_width="fill_parent" android:layout_height="wrap_content"> <EditText android:id="@+id/url_field" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="1.0" android:lines="1" /> <Button android:id="@+id/go_button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/go_button" /> </LinearLayout> <WebView android:id="@+id/web_view" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_weight="1.0" /> </LinearLayout>
MyMapWeb.java (1/2) package org.example.mymap; public class MyMapWeb extends MapActivity { private MapView map; private MapController controller; private EditText urltext; private Button gobutton; private WebView webview; @Override public void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); setcontentview(r.layout.main); initmapview(); initmylocation(); urltext = (EditText) findviewbyid(r.id.url_field); gobutton = (Button) findviewbyid(r.id.go_button); webview = (WebView) findviewbyid(r.id.web_view); // Setup event handlers gobutton.setonclicklistener(new OnClickListener() { public void onclick(view view) { openbrowser(); ); urltext.setonkeylistener(new OnKeyListener() { public boolean onkey(view view, int keycode, KeyEvent event) { if (keycode == KeyEvent.KEYCODE_ENTER) { openbrowser(); return true; return false; );
MyMapWeb.java (2/2) /** Open a browser on the URL specified in the text box */ private void openbrowser() { webview.loadurl(urltext.gettext().tostring()); webview.requestfocus(); /** Find and initialize the map view. */ private void initmapview() { map = (MapView) findviewbyid(r.id.map); controller = map.getcontroller(); map.setsatellite(true); map.setbuiltinzoomcontrols(true); /** Start tracking the position on the map. */ private void initmylocation() { final MyLocationOverlay overlay = new MyLocationOverlay(this, map); overlay.enablemylocation(); //overlay.enablecompass(); // does not work in emulator overlay.runonfirstfix(new Runnable() { public void run() { // Zoom in to current location controller.setzoom(8); controller.animateto(overlay.getmylocation()); ); map.getoverlays().add(overlay); @Override protected boolean isroutedisplayed() { return false; // Required by MapActivity
MyMapWeb 실행화면
요약 GPS 장치를통해위치를인식하는방법에대해서알아보았다. 가속도계에대해서알아보았다. 지도를나타내는맵뷰에대해알아보고, 실제프로그래밍에적용해보았다. 웹뷰와맵뷰를결합한프로그램을작성함으로써, 여러 API 들을연결하여정보들을결합하는매시업 (mashup) 의가능성에대해알아보았다.
퀴즈 위치정보를위해서는 AndroidManifest.xml 에어떤것을추가해야하는가? 인터넷사용을위해서는 AndroidManifest.xml 에어떤것을추가해야하는가? 액티비티내에서 LocationManager 클래스를반환받기위해서는어떤메서드를사용하는가? LocationManager.getBestProvider() 의선정기준에는어떠한것들이있는가? 위치업데이트에는배터리가많이소모된다. 이를어떻게줄일수있는가? 에뮬레이터가돌아가는경우, GPS 정보가제공되지않을수있다. 이를해결하는방법들에는어떤것들이있는가? 액티비티내에서 SensorManager 클래스를반환받기위해서는어떤메서드를사용하는가? 역시에뮬레이터의경우, 센서가없을수있다. 이럴때, 사용자가임의로센서의값들을입력하기위해서는어떻게해야하는가? 구글맵을실행하려면처음에프로젝트를어떻게생성해야하는가? 왜구글맵은안드로이드코어의일부가아닌가? 구글맵 API 키에는어떠한것들이있고, 이를얻기위해서는어떻게해야하는가? 안드로이드운영체제에서 Home 의배경화면을어떻게바꾸는가? 만일자신만의사진으로배경화면을하고싶다면어떻게해야하는가?
연습문제 웹뷰와맵뷰를같이쓰는예제를스스로처음부터구현해보자. 맵뷰에서사용자가지도의특정부분에가거나, 특정부분을클릭했을때, 이에대한이벤트나정보를받으려면어떻게해야하는지, 그게가능한지아닌지를 MapView 레퍼런스매뉴얼에서검색해보자. 웹뷰와맵뷰를같이쓰는예제에서, 웹뷰대신배경에컬러를서로다르게하는텍스트뷰나다른위젯들을사용해보자. 다음지도 API 나네이버지도 API 를이용하는안드로이드응용프로그래밍에시도해보자. 센서의값들에는잡음이들어가기도하고시간적으로불규칙으로들어오는경우가많다. 이러한센서의값을제대로받고해석하기위해서는어떠한방법이요구되는가? 평활화는어떻게수행하는게효과적일까?